#include "RuleHandler.h"
#include <iostream>
#include <fstream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <sys/time.h>

#define RULESIZE 100
#define SUBRULESIZE 100
#define PHRASESIZE 100
#define SUBRULENUM 32

using namespace std;

extern FILE* popen(const char* cmd, const char* mode);

// add ruleset folder
void addCat(const char* cat) {
  if (strlen(cat) == 0 || catExists(cat)) {
    return;
  }
  
  ofstream rules;
  rules.open("../users/rules/rules.txt", fstream::app);
  rules << cat << endl;
  rules.close();
  
  char cmd[RULESIZE + 30];
  sprintf(cmd, "mkdir ../users/rules/%s", cat);
  system(cmd);

  // make cat file for storing subcats
  sprintf(cmd, "../users/rules/%s/%s", cat, cat);
  rules.open(cmd, fstream::app);
  rules.close();

  return;
}

// remove ruleset folder
void removeCat(const char* cat) {
  if (!catExists(cat)) {
    return;
  }

  char rule[RULESIZE];
  ifstream rules;
  ofstream temprules;
  rules.open("../users/rules/rules.txt");
  temprules.open("../users/rules/temprules.txt");
  rules.getline(rule, RULESIZE);
  while (!rules.eof()) {
    if (strcmp(cat, rule) != 0) {
      temprules << rule << endl;
    }
    rules.getline(rule, RULESIZE);
  }
  rules.close();
  temprules.close();
  //remove("../users/rules/rules.txt");
  rename("../users/rules/temprules.txt", "../users/rules/rules.txt");

  sprintf(rule, "rm -rf ../users/rules/%s", cat);
  system(rule);

  return;
}

// finds if ruleset exists
bool catExists(const char* cat) {
  if (strlen(cat) == 0) {
    return false;
  }
  char rule[RULESIZE];
  
  ifstream rules;
  rules.open("../users/rules/rules.txt");
  while (!rules.eof()) {
    rules.getline(rule, RULESIZE);
    if (strcmp(cat, rule) == 0) {
      rules.close();
      return true;
    }
  }
  rules.close();
  return false;
}

// add subcategory for ruleset w/ given weight
void addSubcat(const char* cat, const char* subcat, float weight) {
  if (!catExists(cat) || subcatExists(cat, subcat)) {
    return;
  }
  char filepath[30 + 2*RULESIZE];
  
  ofstream subrules;
  sprintf(filepath, "../users/rules/%s/%s", cat, cat);
  subrules.open(filepath, fstream::app);
  subrules << weight << "\t" << subcat << endl;
  subrules.close();

  // make subcat file for storing phrases
  sprintf(filepath, "../users/rules/%s/%s.txt", cat, subcat);
  subrules.open(filepath, fstream::app);
  subrules.close();

  return;
}

// remove subcategory for ruleset
void removeSubcat(const char* cat, const char* subcat) {
  if (!subcatExists(cat, subcat)) {
    return;
  }
  char filepath[30 + 2*RULESIZE];
  char tempfilepath[30 + 2*RULESIZE];

  char subrule[SUBRULESIZE];
  float weight;
  char subgroup[SUBRULESIZE];

  ifstream subrules;
  ofstream tempsubrules;
  sprintf(filepath, "../users/rules/%s/%s", cat, cat);
  subrules.open(filepath);
  sprintf(tempfilepath, "../users/rules/%s/temp%s", cat, cat);
  tempsubrules.open(tempfilepath);
  subrules.getline(subrule, SUBRULESIZE);
  while (!subrules.eof()) {
    sscanf(subrule, "%f\t%s", &weight, subgroup);
    if (strcmp(subcat, subgroup) != 0) {
      tempsubrules << subrule << endl;
    }
    subrules.getline(subrule, SUBRULESIZE);
  }
  subrules.close();
  tempsubrules.close();
  //remove("../users/rules/rules.txt");
  rename(tempfilepath, filepath);

  char cmd[30 + RULESIZE + SUBRULESIZE];
  sprintf(cmd, "rm -rf ../users/rules/%s/%s.txt", cat, subcat);
  system(cmd);

  return;
}

// modify weight of a ruleset subcategory
// returns previous weight
float changeSubcatWeight(const char* cat, const char* subcat, float weight) {
  if (!subcatExists(cat, subcat)) {
    return 0;
  }
  char filepath[30 + 2*RULESIZE];
  char tempfilepath[30 + 2*RULESIZE];

  char subrule[SUBRULESIZE];
  float subgroupweight;
  char subgroup[SUBRULESIZE];
  float oldweight;

  ifstream subrules;
  ofstream tempsubrules;
  sprintf(filepath, "../users/rules/%s/%s", cat, cat);
  subrules.open(filepath);
  sprintf(tempfilepath, "../users/rules/%s/temp%s", cat, cat);
  tempsubrules.open(tempfilepath);
  subrules.getline(subrule, SUBRULESIZE);
  while (!subrules.eof()) {
    sscanf(subrule, "%f\t%s", &subgroupweight, subgroup);
    if (strcmp(subcat, subgroup) != 0) {
      tempsubrules << subrule << endl;
    }
    else {
      tempsubrules << weight << "\t" << subgroup << endl;
      oldweight = subgroupweight;
    }
    subrules.getline(subrule, SUBRULESIZE);
  }
  subrules.close();
  tempsubrules.close();
  rename(tempfilepath, filepath);
  return oldweight;
}

// finds if subcategory exists in ruleset
bool subcatExists(const char* cat, const char* subcat) {
  if (strlen(subcat) == 0 || !catExists(cat)) {
    return false;
  }
  char filepath[30 + 2*RULESIZE];
  char subrule[30 + SUBRULESIZE];
  float subgroupweight;
  char subgroup[SUBRULESIZE];

  ifstream subrules;
  sprintf(filepath, "../users/rules/%s/%s", cat, cat);
  subrules.open(filepath);
  while (!subrules.eof()) {
    subrules.getline(subrule, 30 + SUBRULESIZE);
    sscanf(subrule, "%f\t%s", &subgroupweight, subgroup);
    if (strcmp(subcat, subgroup) == 0) {
      subrules.close();
      return true;
    }
  }
  subrules.close();
  return false;
}

// adds phrase/keyword to subcategory w/ given weight
void addPhrase(const char* cat, const char* subcat, const char* phrase, float weight) {
  if (!subcatExists(cat, subcat) || phraseExists(cat, subcat, phrase)) {
    return;
  }
  char filepath[30 + RULESIZE + SUBRULESIZE];
  
  ofstream phrases;
  sprintf(filepath, "../users/rules/%s/%s.txt", cat, subcat);
  phrases.open(filepath, fstream::app);
  phrases << weight << "\t" << phrase << endl;
  phrases.close();

  return;
}

// removes phrase/keyword from category
void removePhrase(const char* cat, const char* subcat, const char* phrase) {
  if (!phraseExists(cat, subcat, phrase)) {
    return;
  }
  char filepath[30 + RULESIZE + SUBRULESIZE];
  char tempfilepath[30 + RULESIZE + SUBRULESIZE];

  char fullphrase[30 + PHRASESIZE];
  float phraseweight;
  char phrasename[PHRASESIZE];

  ifstream phrases;
  ofstream tempphrases;
  sprintf(filepath, "../users/rules/%s/%s.txt", cat, subcat);
  phrases.open(filepath);
  sprintf(tempfilepath, "../users/rules/%s/temp%s", cat, subcat);
  tempphrases.open(tempfilepath);
  phrases.getline(fullphrase, 30 + PHRASESIZE);
  while (!phrases.eof()) {
    sscanf(fullphrase, "%f\t%s", &phraseweight, phrasename);
    if (strcmp(phrase, phrasename) != 0) {
      tempphrases << fullphrase << endl;
    }
    phrases.getline(fullphrase, 30 + PHRASESIZE);
  }
  phrases.close();
  tempphrases.close();
  rename(tempfilepath, filepath);

  return;
}

// modify weight of phrase in given ruleset subcategory
// returns previous weight
float changePhraseWeight(const char* cat, const char* subcat, const char* phrase, float weight) {
  if (!phraseExists(cat, subcat, phrase)) {
    return 0;
  }
  char filepath[30 + RULESIZE + SUBRULESIZE];
  char tempfilepath[30 + RULESIZE + SUBRULESIZE];

  char fullphrase[30 + PHRASESIZE];
  float phraseweight;
  char phrasename[PHRASESIZE];
  float oldweight;

  ifstream phrases;
  ofstream tempphrases;
  sprintf(filepath, "../users/rules/%s/%s.txt", cat, subcat);
  phrases.open(filepath);
  sprintf(tempfilepath, "../users/rules/%s/temp%s", cat, subcat);
  tempphrases.open(tempfilepath);
  phrases.getline(fullphrase, 30 + PHRASESIZE);
  while (!phrases.eof()) {
    sscanf(fullphrase, "%f\t%s", &phraseweight, phrasename);
    if (strcmp(phrase, phrasename) != 0) {
      tempphrases << fullphrase << endl;
    }
    else {
      tempphrases << weight << "\t" << phrasename << endl;
      oldweight = phraseweight;
    }
    phrases.getline(fullphrase, 30 + PHRASESIZE);
  }
  phrases.close();
  tempphrases.close();
  rename(tempfilepath, filepath);
  return oldweight;
}

// finds if phrase/keyword exists in given ruleset subcategory
bool phraseExists(const char* cat, const char* subcat, const char* phrase) {
  if (strlen(phrase) == 0 || !subcatExists(cat, subcat)) {
    return false;
  }
  char filepath[30 + RULESIZE + SUBRULESIZE];
  char fullphrase[30 + PHRASESIZE];
  float phraseweight;
  char phrasename[PHRASESIZE];

  ifstream phrases;
  sprintf(filepath, "../users/rules/%s/%s.txt", cat, subcat);
  phrases.open(filepath);
  while (!phrases.eof()) {
    phrases.getline(fullphrase, 30 + PHRASESIZE);
    sscanf(fullphrase, "%f\t%s", &phraseweight, phrasename);
    if (strcmp(phrase, phrasename) == 0) {
      phrases.close();
      return true;
    }
  }
  phrases.close();
  return false;
}

// applies ruleset and counts weighted score
// prints to cout: score and textfile lines containing phrases/keywords in html
void applyRule(const char* cat, const char* user, const char* filename, bool isTemp) {
  if (!catExists(cat)) {
    return;
  }
  timeval t1, t2;
  ofstream log;
  time_t t;
  time(&t);
  gettimeofday(&t1, NULL);

  extern FILE* popen(const char* cmd, const char* mode);
  FILE *cmdout;
  char cmdbuff[512];
  
  cout << "<pre>";
  char precmd[30 + SUBRULENUM * SUBRULESIZE] = "grep -Ein --color \"";
  bool premult;
  float score;

  char filepath[30 + 2*RULESIZE + SUBRULESIZE];
  char subrule[30 + SUBRULESIZE];
  float subgroupweight;
  char subgroup[SUBRULESIZE];

  ifstream subrules;
  sprintf(filepath, "../users/rules/%s/%s", cat, cat);
  subrules.open(filepath);
  subrules.getline(subrule, 30 + SUBRULESIZE);
  while (!subrules.eof()) {  
    sscanf(subrule, "%f\t%s", &subgroupweight, subgroup);
    
    char fullphrase[30 + PHRASESIZE];
    float phraseweight;
    char phrasename[PHRASESIZE];

    ifstream phrases;
    sprintf(filepath, "../users/rules/%s/%s.txt", cat, subgroup);
    phrases.open(filepath);
    phrases.getline(fullphrase, 30 + PHRASESIZE);
    while (!phrases.eof()) { 
      sscanf(fullphrase, "%f\t%s", &phraseweight, phrasename);
      if (premult) {
	strcat(precmd, "|");
      }
      else {
	premult = true;
      }
      strcat(precmd, phrasename);

      sprintf(filepath, "../users/%s", filename);
      char grepphrase[512] = "grep -Eoin \"";
      strcat(grepphrase, phrasename);
      strcat(grepphrase, "\" ");
      strcat(grepphrase, filepath);
      strcat(grepphrase, " | wc -l");

      if (!(cmdout = popen((const char*) grepphrase, "r"))) {
	return;
      }
      fgets(cmdbuff, sizeof(cmdbuff), cmdout);
      score += subgroupweight * phraseweight * atoi(cmdbuff);
      pclose(cmdout);
      phrases.getline(fullphrase, 30 + PHRASESIZE);
    }
    phrases.close();
    subrules.getline(subrule, 30 + SUBRULESIZE);
  }
  subrules.close();

  strcat(precmd, "\" ");
  sprintf(filepath, "../users/%s", filename);
  strcat(precmd, filepath);

  cout << "<pre>";
  if (!(cmdout = popen((const char*) precmd, "r"))) {
    return;
  }
  while (fgets(cmdbuff, sizeof(cmdbuff), cmdout)) {
    printf("%s", cmdbuff);
  }
  pclose(cmdout);
  cout << "</pre>";

  cout << "Total score: " << score << "<br>";
  gettimeofday(&t2, NULL);
  cout << "Search took " << (double) (t2.tv_sec - t1.tv_sec) + (double) (t2.tv_usec - t1.tv_usec) / 1000000 << " seconds." << endl;

  if (!isTemp) {
    char fileowner[100];
    char file[100];
    sscanf(filename, "%[^/]/%s", fileowner, file);
    sprintf(filepath, "../users/logs@%s/filelog-%s.txt", fileowner, file);
    log.open(filepath, fstream::app);
    log << "Analyzed by " << user << " on " << asctime(localtime(&t)) << " from " << getenv("REMOTE_ADDR") << endl;
    log.close();
  }

  return;
}
